/* 
 * File:   ParticleSystem.h
 * Author: RedEyedKiller
 *
 * Created on 14 Ιούλιος 2010, 1:21 πμ
 */

#ifndef PARTICLESYSTEM_H
#define	PARTICLESYSTEM_H

#include <vector>
#include "../SelfDrawable.h"
#include "../Factory.h"
#include "ParticleEmiter.h"

namespace physicsSystem
{

namespace particleSystem
{

class ParticleEmiter;
class Particle;


/**
 * This class stores simmilar ParticleEmiters. Provides an interface to manipulate them.
 * Also this is mainly responsible for the blitting of the particles and in order
 * to blit the any instance of this should be registered in the rendering manager.
 *
 * Further more emiters are managed using a object pool so any emiter killed will not
 * be deleted but set to inactive until a new is required. The the inactive will
 * be reset to be used as a new one.
 * Note that there is a limit up to how many inactive emiters there can be simultaneously
 * described by ParticleSystem::maxCachedEmiters.
 */
class ParticleSystem : public SelfDrawable
{
public:
    
    enum CameraBehaviour
    {
        CB_NORMAL,
        CB_FOLLOW,
        CB_WRAP
    };
    
    ParticleSystem();
    virtual ~ParticleSystem();
    //adds a new emiter
    int AddPrototypedEmiterAt(const Math::Vector2F& pos, const Math::Vector2F& dir, bool oneTime = false);
    int FindNextRemoved(int from);
    
    /**
     * The main method for rendering particles.
     * Calls all ParticleEmiter::Render methods.
     * @param gCore
     * @param camera
     */
    virtual void Draw(sdl::GraphicsCore *gCore, Camera *camera = 0);
    //This is called by ParticleSystemManager or PhysicsManager
    void ApplyForcesToParticles(forces::Force* force);
    void UpdateParticles(unsigned long deltaTime);
    void AddParticles();

    /**
     * Checks if any particle of this system is colliding with a tile.
     */
    void TileCollision(LevelMap* map);

    /**
     * If a dead emiter is found this must be called with its id as argument.
     */
    inline void SubmitDeath(int id)
    {
        RemoveLastEmiters( );

        unsigned uID = static_cast<unsigned>( id );

        if( uID < maxCachedEmiters && firstRemovedId > id )
        {
            firstRemovedId = id;
        }
        else if( firstRemovedId == -1 )
        {
            firstRemovedId = id;
        }
    }
    
    void RemoveLastEmiters();
    
    void MoveEmiters(float x, float y);
    
    void MoveManipulators(float x, float y);
    
    void MoveAllParticles(float x, float y);
    
    void MoveSystem(float x, float y, bool moveParticles);
    
    bool KillEmiter(int id);
    
    // <editor-fold defaultstate="collapsed" desc="get set">
    void SetName(std::string& name);
    std::string GetName() const;
    void SetFirstRemovedId(int firstRemovedId);
    int GetFirstRemovedId() const;
    void SetLastInUseId(int lastInUseId);
    int GetLastInUseId() const;
    void SetCreationThreashold(int creationThreashold);
    int GetCreationThreashold() const;
    void SetPartsPerEmiter(unsigned long int partsPerEmiter);
    unsigned long int GetPartsPerEmiter() const;
    void SetCurrentPart(unsigned long int currentPart);
    unsigned long int GetCurrentPart() const;
    void SetMaxParts(unsigned long int maxParts);
    unsigned long int GetMaxParts() const;
    void SetPrototype(ParticleEmiterPrototype prototype);
    ParticleEmiterPrototype GetPrototype() const;
    void SetActive(bool active);
    bool IsActive() const;
    void SetResource(std::string& resource);
    const char* GetResource() const;
    //TODO: Rotate emiter
    // inc dec intensity of manipulators
    int GetEmiterId();
    bool GetEmiterId(int* id);
    void SetMaxCachedEmiters(int maxCachedEmiters);
    int GetMaxCachedEmiters() const;
    void SetMapCollisions(bool mapCollisions);
    bool IsMapCollisions() const;
    void SetCameraBeh(CameraBehaviour cameraBeh);
    CameraBehaviour GetCameraBeh() const;
    // </editor-fold>
private:

    inline void AddNewEmiter(ParticleEmiter* emiter)
    {
        emiters.push_back( emiter );
    }

    inline void ReplaceEmiter(const Vector2F& pos, const Vector2F& dir, int id, bool wipe = false)
    {
        ParticleEmiter* old = emiters[id];
        old->Resurect( pos, dir, id, wipe );
    }

    //data collections
    ParticleEmiterPrototype prototype;
    std::vector<ParticleEmiter*> emiters;
    //system name used as id
    std::string name;
    std::string resource;
    unsigned long int partsPerEmiter;
    int creationThreashold;
    //id managment
    int lastInUseId;
    int firstRemovedId;
    //system will not deallocate emiters if total emiters is less than maxCachedEmiters.
    unsigned maxCachedEmiters;
    bool active;
    
    /**
     * The way system responds to camera movement.
     */
    CameraBehaviour cameraBeh;
    
    bool mapCollisions;
};

};
};
#endif	/* PARTICLESYSTEM_H */

